package com.ovopark.firecar.okhttp;

import android.os.Handler;
import android.util.Log;


import com.ovopark.firecar.utils.JsonUtil;
import com.socks.library.KLog;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.RequestBody;

/**
 * 请求发送者，自动附带uuid失效验证处理
 * 封装请求数据，转化url到retrofit通用post/get方法来执行.
 * @author caixingming 2020/10/15.
 */
public class RequestSender {

    private final static String TAG = "RequestSender";

    private final static int DEFAULT_TIMEOUT_MS = 15000;

    private final String mDefaultServiceError = "连接失败，请检查网络后重试！";
    /**
     * 封装抽象方法类. Post/GET/Upload.
     */
    private RetrofitService mRetrofitService;

    private static RequestSender mRequestSender;

    /**
     * RxJava同意管理网络请求，可以取消网络请求 when OnDestroy() are invoked.
     */
    private CompositeDisposable mCompositeDisposable;

    private HashMap<String,Disposable> mCacheHashMap = new HashMap<>();

    private Handler mHandler;

    private RequestSender() {
        newRetrofitInstance();
        mHandler = new Handler();
        mCompositeDisposable = new CompositeDisposable();
        //全局统一处理消费rxjava产生的错误发射信息，否则会引起崩溃.
        RxJavaPlugins.setErrorHandler(throwable -> {
            if(null != throwable){
                KLog.e(TAG,"some uknown throwable:"+throwable.getMessage());
            }
        });
    }

    /**
     * 更新retrofitservice.
     */
    public void newRetrofitInstance(){
        mRetrofitService = RsGenerator.create(RetrofitService.class);
    }

    /**
     * 单例实现 .
     * @return
     */
    public static RequestSender getInstance(){
        if(null == mRequestSender){
            synchronized (RequestSender.class){
                if(null == mRequestSender){
                    mRequestSender = new RequestSender();
                }
            }
        }

        return mRequestSender;
    }

    /**
     * 发送一般POST请求
     *
     * @param requestData
     */
    public boolean sendRequest(final RequestData requestData) {
        KLog.d(TAG, "sendRequest:" + requestData.toString());
        //处理带头部请求的.
        Map<String,String> headers = requestData.getHeaders();
        if(headers != null){
            return sendRequestWithHeaders(requestData);
        }

        requestData.addSendCount();
        requestData.setStartTime();

        Disposable disposable =  mRetrofitService.postRequest(requestData.getUrl(), requestData.getParam())
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<JSONObject>() {
                    @Override
                    public void accept(final JSONObject response) throws Exception {
                        Log.d(TAG, "+onResponse:" + response);
                        String result = response.optString("result");
                        if (requestData.isToShowLoading()) {
                            long spendTime = System.currentTimeMillis() - requestData.getStartTime();
                            if (spendTime > 500) {
                                requestData.getListener().onResponse(response);
                            } else {
                                mHandler.postDelayed(new Runnable() {
                                    @Override
                                    public void run() {
                                        requestData.getListener().onResponse(response);
                                    }
                                }, 500 - spendTime);
                            }
                        } else {
                            requestData.getListener().onResponse(response);
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "+onErrorResponse:" + throwable);
                        requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                    }
                });
        mCompositeDisposable.add( disposable);
        mCacheHashMap.put(requestData.getUrl(), disposable);
        return true;
    }

    /**
     * 发送一般POST请求带headers.
     *
     * @param requestData
     */
    private boolean sendRequestWithHeaders(final RequestData requestData) {
        Log.d(TAG, "sendRequestWithHeaders:"+ requestData.toString());

        Map<String,String> headers = requestData.getHeaders();
        if(headers == null){
            headers = new HashMap<>();
        }

        if(headers.containsKey("Content-Type")&&headers.get("Content-Type").contains("application/json")){
            return sendRequestRaw(requestData);
        }

        requestData.addSendCount();
        requestData.setStartTime();

        Log.d(TAG, "mRetrofitService:"+mRetrofitService);
        if(mRetrofitService == null){
            Log.d(TAG, "mRetrofitService is NULL return!:");
            return false;
        }

        Disposable disposable =  mRetrofitService.postRequestHeaders(requestData.getUrl(), requestData.getParam() ,headers)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<JSONObject>() {
                    @Override
                    public void accept(final JSONObject response) throws Exception {
                        Log.d(TAG, "+onResponse:" + response);
                        String result = response.optString("result");
                        if (requestData.isToShowLoading()) {
                            long spendTime = System.currentTimeMillis() - requestData.getStartTime();
                            if (spendTime > 500) {
                                requestData.getListener().onResponse(response);
                            } else {
                                mHandler.postDelayed(new Runnable() {
                                    @Override
                                    public void run() {
                                        requestData.getListener().onResponse(response);
                                    }
                                }, 500 - spendTime);
                            }
                        } else {
                            requestData.getListener().onResponse(response);
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "+onErrorResponse:" + throwable);
                        requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                    }
                });
        mCompositeDisposable.add( disposable);
        mCacheHashMap.put(requestData.getUrl(), disposable);
        return true;
    }


    /**
     * 发送Raw类型的RequestBody.
     * @param requestData
     * @return
     */
    private boolean sendRequestRaw(RequestData requestData) {
        Log.d(TAG, "sendRequestRaw:"+ requestData.toString());
        requestData.addSendCount();
        requestData.setStartTime();

        Map<String,String> headers = requestData.getHeaders();
        if(headers == null){
            headers = new HashMap<>();
        }
        RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), JsonUtil.toJSONString(requestData.getParam()));
        Disposable disposable =  mRetrofitService.postRequest(requestData.getUrl(), body ,headers)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<JSONObject>() {
                    @Override
                    public void accept(final JSONObject response) throws Exception {
                        Log.d(TAG, "+onResponse:" + response);
                        String result = response.optString("result");
                        if (requestData.isToShowLoading()) {
                            long spendTime = System.currentTimeMillis() - requestData.getStartTime();
                            if (spendTime > 500) {
                                requestData.getListener().onResponse(response);
                            } else {
                                mHandler.postDelayed(new Runnable() {
                                    @Override
                                    public void run() {
                                        requestData.getListener().onResponse(response);
                                    }
                                }, 500 - spendTime);
                            }
                        } else {
                            requestData.getListener().onResponse(response);
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "+onErrorResponse:" + throwable);
                        requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                    }
                });
        mCompositeDisposable.add( disposable);
        mCacheHashMap.put(requestData.getUrl(), disposable);
        return true;
    }



    /**
     * 发送Raw类型的RequestBody.
     * @param requestData
     * @return
     */
    public boolean sendRequestRaw(RequestData requestData ,RequestBody requestBody) {
        Log.d(TAG, "sendRequestRaw:"+ requestData.toString());
        requestData.addSendCount();
        requestData.setStartTime();

        Map<String,String> headers = requestData.getHeaders();
        if(headers == null){
            headers = new HashMap<>();
        }
        Disposable disposable =  mRetrofitService.postRequest(requestData.getUrl(), requestBody ,headers)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<JSONObject>() {
                    @Override
                    public void accept(final JSONObject response) throws Exception {
                        Log.d(TAG, "+onResponse:" + response);
                        String result = response.optString("result");
                        if (requestData.isToShowLoading()) {
                            long spendTime = System.currentTimeMillis() - requestData.getStartTime();
                            if (spendTime > 500) {
                                requestData.getListener().onResponse(response);
                            } else {
                                mHandler.postDelayed(new Runnable() {
                                    @Override
                                    public void run() {
                                        requestData.getListener().onResponse(response);
                                    }
                                }, 500 - spendTime);
                            }
                        } else {
                            requestData.getListener().onResponse(response);
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "+onErrorResponse:" + throwable);
                        requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                    }
                });
        mCompositeDisposable.add( disposable);
        mCacheHashMap.put(requestData.getUrl(), disposable);
        return true;
    }


    /**
     * 上传文件.
     * @param requestData
     * @return
     */
    public boolean upLoadRequestRaw(RequestData requestData ,RequestBody requestBody) {
        Log.d(TAG, "upLoadRequestRaw:"+ requestData.toString());
        requestData.addSendCount();
        requestData.setStartTime();

        Map<String,String> headers = requestData.getHeaders();
        if(headers == null){
            headers = new HashMap<>();
        }
        Disposable disposable =  mRetrofitService.postRequestUpload(requestData.getUrl(), requestBody ,headers)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<JSONObject>() {
                    @Override
                    public void accept(final JSONObject response) throws Exception {
                        Log.d(TAG, "+onResponse:" + response);
                        String result = response.optString("result");
                        if (requestData.isToShowLoading()) {
                            long spendTime = System.currentTimeMillis() - requestData.getStartTime();
                            if (spendTime > 500) {
                                requestData.getListener().onResponse(response);
                            } else {
                                mHandler.postDelayed(new Runnable() {
                                    @Override
                                    public void run() {
                                        requestData.getListener().onResponse(response);
                                    }
                                }, 500 - spendTime);
                            }
                        } else {
                            requestData.getListener().onResponse(response);
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "+onErrorResponse:" + throwable);
                        requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                    }
                });
        mCompositeDisposable.add( disposable);
        mCacheHashMap.put(requestData.getUrl(), disposable);
        return true;
    }

    /**
     * 发送一般GET请求
     *
     * @param requestData
     */
    public boolean sendGetRequest(final RequestData requestData) {
        Disposable disposable;
        requestData.addSendCount();
        requestData.setStartTime();

        String url = requestData.mUrl + requestData.getParams();
        KLog.d(TAG, "sendGetRequest:" + url);

        if(requestData.getHeaders()!=null && requestData.getHeaders().size()>0){
            KLog.i(TAG,"send with head contentType:"+requestData.getHeaders().get("Content-Type"));
            disposable = mRetrofitService.getRequest(url,requestData.getHeaders())
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<JSONObject>() {
                        @Override
                        public void accept(final JSONObject response) throws Exception {
                            handlerGetSuccess(response, requestData);
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            KLog.e(TAG, "+onErrorResponse:" + throwable);
                            requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                        }
                    });
        }else{
            KLog.i(TAG,"send with out head contentType!");
            disposable = mRetrofitService.getRequest(url)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<JSONObject>() {
                        @Override
                        public void accept(final JSONObject response) throws Exception {
                            handlerGetSuccess(response, requestData);
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            KLog.e(TAG, "+onErrorResponse:" + throwable);
                            requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                        }
                    });
        }

        mCompositeDisposable.add(disposable);
        mCacheHashMap.put(requestData.getUrl(), disposable);

        return true;
    }

    private void handlerGetSuccess(JSONObject response, RequestData requestData) {
        String result = response.optString("result");
        if (requestData.isToShowLoading()) {
            long spendTime = System.currentTimeMillis() - requestData.getStartTime();
            if (spendTime > 500) {
                requestData.getListener().onResponse(response);
            } else {
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        requestData.getListener().onResponse(response);
                    }
                }, 500 - spendTime);
            }
        } else {
            requestData.getListener().onResponse(response);
        }
    }




    /**
     * 发送一般PUT请求
     *
     * @param requestData
     */
    public boolean sendPutRequest(final RequestData requestData) {
        Disposable disposable;
        requestData.addSendCount();
        requestData.setStartTime();

        String url = requestData.mUrl + requestData.getParams();
        KLog.d(TAG, "sendGetRequest:" + url);

        if(requestData.getHeaders()!=null && requestData.getHeaders().size()>0){
            disposable = mRetrofitService.putRequest(url,requestData.getHeaders())
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<JSONObject>() {
                        @Override
                        public void accept(final JSONObject response) throws Exception {
                            handlerGetSuccess(response, requestData);
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            Log.e(TAG, "+onErrorResponse:" + throwable);
                            requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                        }
                    });
        }else{
            disposable = mRetrofitService.getRequest(url)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<JSONObject>() {
                        @Override
                        public void accept(final JSONObject response) throws Exception {
                            handlerGetSuccess(response, requestData);
                        }
                    }, new Consumer<Throwable>() {
                        @Override
                        public void accept(Throwable throwable) throws Exception {
                            Log.e(TAG, "+onErrorResponse:" + throwable);
                            requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                        }
                    });
        }

        mCompositeDisposable.add(disposable);
        mCacheHashMap.put(requestData.getUrl(), disposable);

        return true;
    }

    /**
     * 获取返回集合Array的json数据.
     * @param requestData
     * @return
     */
    public boolean sendPostArrayRequest(final RequestData<JSONArray> requestData) {

        requestData.addSendCount();
        requestData.setStartTime();

        String url = requestData.mUrl;
        KLog.d(TAG, "sendPostArrayRequest:" + url);

        Map<String,String> headers = requestData.getHeaders();
        if(headers == null){
            headers = new HashMap<>();
        }

        Disposable disposable = mRetrofitService.postRequestJsonArray(requestData.getUrl(),requestData.getParam(),headers)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<JSONArray>() {
                    @Override
                    public void accept(final JSONArray response) throws Exception {
//                        String result = response.optString("result");
                        if (null != response) {//返回结果不为空.
                            if (requestData.isToShowLoading()) {
                                long spendTime = System.currentTimeMillis() - requestData.getStartTime();
                                if (spendTime > 500) {
                                    requestData.getListener().onResponse(response);
                                } else {
                                    mHandler.postDelayed(new Runnable() {
                                        @Override
                                        public void run() {
                                            requestData.getListener().onResponse(response);
                                        }
                                    }, 500 - spendTime);
                                }
                            } else {
                                requestData.getListener().onResponse(response);
                            }
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "+onErrorResponse:" + throwable);
                        requestData.getErrorListener().onErrorResponse(new Throwable(mDefaultServiceError));
                    }
                });
        mCompositeDisposable.add(disposable);
        mCacheHashMap.put(requestData.getUrl(), disposable);
        return true;
    }

    /**
     * 取消所有请求
     * 注意：clear后还可以继续add请求
     * 如果使用了dispose则不可以继续添加网络请求.
     */
    public void stop() {
        KLog.i(TAG,"stop all url()~!");
        mCompositeDisposable.clear();
        mCacheHashMap.clear();
    }

    /**
     * 取消对应tag的请求
     *
     * @param tag
     */
    public void stop(String tag) {
        KLog.i(TAG,"stop url: "+tag);
        Disposable disposable = mCacheHashMap.get(tag);
        if(null != disposable && !disposable.isDisposed()){
            mCompositeDisposable.remove(disposable);
            disposable.dispose();
            mCacheHashMap.remove(tag);
        }
    }


    /**
     * 请求数据
     */
    public static class RequestData<T> {
        private String mUrl;
        private Map<String, String> mParam;//参数集合
        private Map<String, String> mHeaders;//请求头集合
        private Response.Listener<T> mListener;
        private Response.ErrorListener mErrorListener;
        private volatile int mSendCount;
        private Object mTag;

        /**
         * 显示内容加载中
         */
        private boolean mIsToShowLoading;
        private long mStartTime;

        /**
         * 请求是否需要重试。
         */
        private boolean mNeedRetry = true;

        private int mTimeout = -1;

        public RequestData(String url, Map<String, String> param,
                           Response.Listener<T> listener, Response.ErrorListener errorListener) {
            this(url, param, listener, errorListener, true, true);
        }

        public RequestData(String url, Map<String, String> param,Map<String, String> headers,
                           Response.Listener<T> listener, Response.ErrorListener errorListener) {
            this(url, param, listener, errorListener, true, true);
            this.mHeaders = headers;
        }

        public RequestData(String url, Map<String, String> param,
                           Response.Listener<T> listener, Response.ErrorListener errorListener,
                           Object tag) {
            this(url, param, listener, errorListener, true, true);
            this.mTag = tag;
        }

        public RequestData(String url, Map<String, String> param, Response.Listener<T> listener,
                           Response.ErrorListener errorListener, boolean isToShowLoading, boolean needRetry) {
            this.mUrl = url;
            this.mParam = param;
            this.setListener(listener);
            this.setErrorListener(errorListener);
            this.setIsToShowLoading(isToShowLoading);
            this.mNeedRetry = needRetry;
        }

        public RequestData(String url, Map<String, String> param, Response.Listener<T> listener,
                           Response.ErrorListener errorListener, boolean needRetry,
                           Object tag) {
            this(url, param, listener, errorListener, true, needRetry);
            this.mTag = tag;
        }

        public RequestData(String url, Map<String, String> param,
                           Response.Listener<T> listener, Response.ErrorListener errorListener,
                           boolean isToShowLoading) {
            this.mUrl = url;
            this.mParam = param;
            this.setListener(listener);
            this.setErrorListener(errorListener);
            this.setIsToShowLoading(isToShowLoading);
        }

        public void addSendCount() {
            mSendCount++;
        }

        public String getUrl() {
            return mUrl;
        }

        public void setUrl(String url) {
            this.mUrl = url;
        }

        public Map<String, String> getHeaders() {
            return mHeaders;
        }

        public void setHeaders(Map<String, String> mHeaders) {
            this.mHeaders = mHeaders;
        }

        public Map<String, String> getParam() {
            return mParam;
        }

        /**
         * 返回GET拼接参数  ？mid=xxxx&uid=xxxx
         *
         * @return
         */
        public String getParams() {
            if (null != mParam && mParam.size() > 0) {
                StringBuilder result = new StringBuilder("?");
                Object[] keys = mParam.keySet().toArray();
                for (int i = 0; i < keys.length; i++) {
                    if (i > 0) {
                        result.append("&");
                    }
                    result.append(keys[i]).append("=").append(mParam.get(keys[i]));
                }
                return result.toString();
            } else {
                return "";
            }
        }

        public void setParam(Map<String, String> param) {
            this.mParam = param;
        }

        public Response.Listener<T> getListener() {
            return mListener;
        }

        public void setListener(Response.Listener<T> listener) {
            this.mListener = listener;
        }

        public Response.ErrorListener getErrorListener() {
            return mErrorListener;
        }

        public void setErrorListener(Response.ErrorListener errorListener) {
            this.mErrorListener = errorListener;
        }

        public int getSendCount() {
            return mSendCount;
        }

        public void setSendCount(int sendCount) {
            this.mSendCount = sendCount;
        }

        public long getStartTime() {
            return mStartTime;
        }

        public boolean isToShowLoading() {
            return mIsToShowLoading;
        }

        public void setIsToShowLoading(boolean isToShowLoading) {
            this.mIsToShowLoading = isToShowLoading;
        }

        public Object getTag() {
            return mTag;
        }

        public RequestData setTag(Object tag) {
            this.mTag = tag;
            return this;
        }

        public void setStartTime() {
            if (this.mStartTime == 0L) {//未设置开始时间时设置请求时间
                this.mStartTime = System.currentTimeMillis();
            }
        }

        public boolean isNeedRetry() {
            return mNeedRetry;
        }

        public void setNeedRetry(boolean needRetry) {
            this.mNeedRetry = needRetry;
        }

        public int getTimeout() {
            return mTimeout;
        }

        public void setTimeout(int timeout) {
            mTimeout = timeout;
        }

        @Override
        public String toString() {
            return (mUrl + '?' + mParam).replace(", ", "&").replace("{", "").replace("}", "");
        }
    }


    /**
     * 数据请求返回结果.
     * @param <T>
     */
    public interface  CallBack<T>{
        /**
         * 获取结果成功并返回.
         * @param respone
         */
        void onSuccess(T respone);

        /**
         * 错误返回.
         * @param error
         */
        void onError(Throwable error);
    }
}
